
#include <TVout.h>
#include <fontALL.h>
#include "sfdblogo.h"
#include "slam.h"
#include "play.h"
#include "pitches.h"
#include "font12x18.h"

TVout TV;

// PIN 7 and 9 for TVout video
// PIN 11      for TVout audio 
const int REVERSE_PIN = 2;     // +/-
const int HOME_PIN = 12;        // Points for Home (use reverse)
const int VISITORS_PIN = 5;     // Points for Visitors (use reverse)
const int ARROW_PIN = 4;        // Switch arrow (use reverse for bouns 4 config: -- B- -B BB)
const int TIME_PIN = 8;         // Start and Stop time (use reverse for period)
const int ADJUSTTIME_PIN = 10;  // Adjust time (use reverse)
const int TIMELED_PIN = 6;      // Led for time

//Button state
unsigned int home_button_prev = 0;
unsigned int visitors_button_prev = 0;
unsigned int time_button_prev = 0;
unsigned int arrow_button_prev = 0;
unsigned int adjusttime_button_prev = 0;
unsigned long adjusttime_button_first_pression_millis;

//Led state
unsigned int timeled_state = 0;
boolean timeled_blinking = true;
unsigned long timeled_bink_until_millis = 0;

//State variables
unsigned int home_score = 0;
unsigned int visitors_score = 0;
boolean arrow_left = true;
unsigned int period = 1;
unsigned int bonus = 0; // 0:no_bonus 1:home_bonus 2;guest_bonus 3:both_bonus

boolean time_running = false;
boolean time_resetted = true;
unsigned long current_time_millis = 0;
unsigned long previous_time_millis = 0;

unsigned int minutes = 1; //default: 10 minutes per period
unsigned int seconds = 0;
const unsigned int DO_NOT_SHOW = 10;
unsigned int decimal = DO_NOT_SHOW;

unsigned long remain_time_millis = (minutes*60+seconds)*1000; 

void setup() 
{  
  TV.begin(PAL,120,96);
  presentation();
  TV.clear_screen();
  //Logo
  TV.bitmap(0,41,SlamLogo);
  //TV.bitmap(85,45,PlayLogo);
  TV.select_font(font4x6);
  TV.print(0, 90, "by SFDB");
  refresh_screen();
   
  pinMode(REVERSE_PIN, INPUT); 
  pinMode(HOME_PIN, INPUT);
  pinMode(VISITORS_PIN, INPUT);
  pinMode(ARROW_PIN, INPUT);  
  pinMode(TIME_PIN, INPUT);
  pinMode(ADJUSTTIME_PIN, INPUT); 
  pinMode(TIMELED_PIN, OUTPUT);
  digitalWrite(TIMELED_PIN, LOW);
}

void loop() 
{
  boolean need_refresh = false;

  //Manage buttons
  int home_button = digitalRead(HOME_PIN);
  int visitors_button = digitalRead(VISITORS_PIN);
  int reverse_button = digitalRead(REVERSE_PIN);
  int time_button = digitalRead(TIME_PIN);
  int arrow_button = digitalRead(ARROW_PIN);
  int adjusttime_button = digitalRead(ADJUSTTIME_PIN);
  
  boolean reverse_pressed = false;
  //Check reverse_button
   if ((reverse_button == HIGH))
      reverse_pressed = true;
  
  //Check time_button
  if ((time_button == HIGH) && (time_button_prev == LOW)) 
  {
    if(reverse_pressed)
    {
      if(period==9)
        period = 1;
      else
        period++;
      need_refresh = true;
    }
    else
    {
      stop_or_start_time();
      need_refresh = true;
    }
  }
  time_button_prev  = time_button;  
  
  //Check arrow_button
  if ((arrow_button == HIGH) && (arrow_button_prev == LOW)) 
  {
    if(reverse_pressed)
    {
      if(bonus==3)
        bonus=0;
      else
        bonus++;
      need_refresh = true;  
    }
    else
    {
      arrow_left = !arrow_left;
    }
    
    need_refresh = true;
  }
  arrow_button_prev  = arrow_button;    
  
  //Check home_button
  if ((home_button == HIGH) && (home_button_prev == LOW)) 
  {
    if(reverse_pressed)
    {
      if(home_score>0)
        home_score--;
    }
    else
      home_score++;
      
    need_refresh = true;
    //delay(50);
  }
  home_button_prev  = home_button;

  //Check visitors_button
  if ((visitors_button == HIGH) && (visitors_button_prev == LOW)) 
  {
    if(reverse_pressed)
    {
      if(visitors_score>0)
        visitors_score--;
    }
    else
      visitors_score++;
    need_refresh = true;
  }
  visitors_button_prev  = visitors_button;  
  
  //Check adjusttime_button (double speed)
  boolean first_pression = false;
  boolean long_pressed = false;
  if (adjusttime_button == HIGH) 
  {
    if(adjusttime_button_prev == LOW)
    {
      first_pression = true;
      adjusttime_button_first_pression_millis = current_time_millis;
    }
    else //HIGH
    {
      if(current_time_millis-adjusttime_button_first_pression_millis>1000)
        long_pressed = true;
    }
  }
  if(first_pression || long_pressed)
  {
    if(!time_running)
    {      
      remain_time_millis = (minutes*60+seconds)*1000UL;
      if(reverse_pressed)
      { 
        if(remain_time_millis<1000)
          remain_time_millis = 0;
        else
          remain_time_millis-=1000;
        
      }
      else
      {
        remain_time_millis+=1000;
      }
      time_resetted = true;
      need_refresh = true;
      if(long_pressed)
        delay(100);
    }
    else
    {
      if(first_pression && reverse_pressed)
        sfdb();
    } 
  }  
  adjusttime_button_prev  = adjusttime_button;  
  
  //Manage timer
  cli();  // disable interrupts
  current_time_millis = millis();
  sei();  // enable interrupts    
  if(time_running)
  {   
    unsigned long m = current_time_millis - previous_time_millis;
    if(m>0 && m>=remain_time_millis)
    {
      remain_time_millis = 0;
      decimal = DO_NOT_SHOW; 
      need_refresh = true;
      horn();
      stop_time();
    }
    else
    {
      remain_time_millis = remain_time_millis - m;
    }
  }
  previous_time_millis = current_time_millis;
  unsigned long previous_seconds = seconds;
  seconds = remain_time_millis/1000UL;
  minutes = seconds / 60UL;
  seconds = seconds % 60UL;
  minutes = minutes % 60UL;
  if(remain_time_millis<=10000 && remain_time_millis>0)
  {
    unsigned long previous_decimal = decimal;
    decimal = ((remain_time_millis-(minutes*60+seconds)*1000)) / 100UL;
    if(previous_decimal!=decimal && time_running)
    {
      invert_led(TIMELED_PIN, timeled_state);
      need_refresh = true;
    }
  }
  else
  {
    decimal = DO_NOT_SHOW; 
    if(previous_seconds!=seconds && time_running)
    {
      //invert_led(TIMELED_PIN, timeled_state);
      timeled_blink(50);
      need_refresh = true;
    }
  }
  
  //Blinking timeled
  if(timeled_blinking)
  {
    if(current_time_millis>timeled_bink_until_millis)
    {
      digitalWrite(TIMELED_PIN, LOW);
      timeled_blinking = false;
    }
  }
  
  if(need_refresh)
  {
    refresh_screen();
  }
  
  //delay(10); 
}

void stop_or_start_time()
{
  if(time_running)
    stop_time();
  else
    start_time();
}

void stop_time()
{
  time_running = false;
  digitalWrite(TIMELED_PIN, LOW);
}

void start_time()
{
  if(time_resetted)
  {
    //remain_time_millis+=999UL;
    time_resetted = false;
  }
  time_running = true;
  //digitalWrite(TIMELED_PIN, HIGH);
}

void timeled_blink(unsigned long millisecons)
{
  timeled_blinking = true;
  timeled_bink_until_millis = current_time_millis + millisecons;
  digitalWrite(TIMELED_PIN, HIGH);  
}

void invert_led(int pin, unsigned int &state)
{
  if(state==LOW)
    state = HIGH;
  else
    state = LOW;
  digitalWrite(pin, state);
}  

void refresh_screen()
{
  unsigned int h = TV.hres();
  unsigned int v = TV.vres();

  //Points
  const int point_x_pos = 46;
  const int point_y_pos = 4;
  const int score_pos = 15;
  TV.select_font(font8x8ext);
  TV.print(point_y_pos, 0, "HOME     GUEST");
  TV.select_font(font8x8ext);
  int pos = score_pos;
  if(home_score>=10)
    pos = pos-4;
  TV.print(pos-4, point_y_pos+6, "  ");
  TV.print(pos, point_y_pos+6, home_score); 
  pos = h-score_pos-12;
  if(visitors_score>=10)
    pos = pos-4;
  TV.print(pos-4, point_y_pos+6, "  ");
  TV.print(pos, point_y_pos+6, visitors_score);
  
  //Arrow
  const int arrow_x_pos = 50;
  const int arrow_y_pos = 5;
  const int arrow_lenght = 15;
  const int arrow_pointer = 5;
  TV.draw_line(arrow_x_pos,arrow_y_pos,arrow_x_pos+arrow_lenght,arrow_y_pos,1);
  
  unsigned int left_color=1;
  unsigned int right_color=0;
  if(arrow_left)
  {
    left_color=0;
    right_color=1;
  }
  TV.draw_line(arrow_x_pos,arrow_y_pos,arrow_x_pos+arrow_pointer,arrow_y_pos-arrow_pointer,left_color);
  TV.draw_line(arrow_x_pos,arrow_y_pos,arrow_x_pos+arrow_pointer,arrow_y_pos+arrow_pointer,left_color);
  TV.draw_line(arrow_x_pos+arrow_lenght,arrow_y_pos,arrow_x_pos+arrow_lenght-arrow_pointer,arrow_y_pos-arrow_pointer,right_color);
  TV.draw_line(arrow_x_pos+arrow_lenght,arrow_y_pos,arrow_x_pos+arrow_lenght-arrow_pointer,arrow_y_pos+arrow_pointer,right_color); 
  
  //Bouns
  const int bonus_x_pos = 46;
  const int bonus_y_pos = arrow_y_pos + 20;
  const int circle_pos = 35;
  const int circle_radius = 3;
  TV.select_font(font6x8);
  TV.print(bonus_x_pos, bonus_y_pos, "bonus");
  unsigned int fillcolor;
  if(bonus==0 || bonus==2)
    fillcolor = 0;
  else
    fillcolor = 1;
  TV.draw_circle(circle_pos,bonus_y_pos+circle_radius,circle_radius,1,fillcolor);
  if(bonus==0 || bonus==1)
    fillcolor = 0;
  else
    fillcolor = 1;
  TV.draw_circle(h-circle_pos,bonus_y_pos+circle_radius,circle_radius,1,fillcolor);
  
  //Time
  const int time_x_pos = 46;
  const int time_y_pos = bonus_y_pos+20;
  TV.select_font(font6x8);
  
  TV.set_cursor(time_x_pos, time_y_pos);
  TV.print("Time");
  //TV.print(time_x_pos, time_y_pos, "Time");
  TV.select_font(font4x6);
  if(time_running)
    TV.print("          ");
  else
    TV.print(" (stopped)");
    
  TV.select_font(font8x8ext);
  TV.set_cursor(time_x_pos-6,time_y_pos+9);
  if(minutes<10)
    TV.print("0");
  TV.print(minutes);
  TV.print(":");
  if(seconds<10)
    TV.print("0");
  TV.print(seconds);
  if(decimal!=DO_NOT_SHOW)
  {
    TV.print(".");
    TV.print(decimal);
  }
  else
    TV.print("  ");
  
  //Period
  const int period_x_pos = 40;
  const int period_y_pos = time_y_pos+30;
  TV.select_font(font6x8);
  TV.print(period_x_pos, period_y_pos, "Period");
  TV.select_font(font8x8);
  TV.print(period_x_pos+13, period_y_pos+8, period);
    
  //Debug
  //!!!!!!!
  TV.select_font(font4x6);
  TV.set_cursor(90,70);
  //TV.print(remain_time_millis); 
}

void presentation()
{
   //Presentation
   TV.select_font(font12x18);
    TV.println("!!!!");
   
  TV.select_font(font6x8);
  TV.set_cursor(45,40);
  TV.println("Zulio\n");
  TV.delay(2000);
  TV.set_cursor(35,50);
  TV.println("presents\n");
  TV.delay(1500);
  TV.clear_screen();
  TV.bitmap(20,30,SFDBLogo);
  TV.delay(2000);
  TV.select_font(font8x8);
  TV.set_cursor(23,60);
  TV.println("SCOREBOARD\n");
  play_melody();
  TV.delay(1000);
  TV.clear_screen(); 
}

void sfdb()
{
  TV.clear_screen();
  TV.bitmap(20,30,SFDBLogo);
  play_melody();
  TV.clear_screen();
  TV.bitmap(0,41,SlamLogo);
  refresh_screen();
}

void play_melody()
{

const int base_millis = 6000;  

/*
int melody[] = {NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};
int noteDurations[] = {4,8,8,4,4,4,4,4};
*/
// notes in the melody:
/*
int melody[] = {NOTE_G2,NOTE_A2,NOTE_B2,NOTE_C3,
                NOTE_G2,NOTE_A2,NOTE_B2,NOTE_C3, 
                NOTE_G2, NOTE_A2,NOTE_B2, NOTE_CS3,
                NOTE_G2, NOTE_A2,NOTE_B2, NOTE_CS3,
                NOTE_G2, NOTE_A2,NOTE_B2, NOTE_DS3};
*/
int melody[] = {NOTE_C3,NOTE_D3,NOTE_E3,NOTE_F3,
                NOTE_C3,NOTE_D3,NOTE_E3,NOTE_F3, 
                NOTE_C3,NOTE_D3,NOTE_E3,NOTE_FS3,
                NOTE_C3,NOTE_D3,NOTE_E3,NOTE_FS3,
                NOTE_G2, NOTE_A2,NOTE_B2, NOTE_DS3};                
int noteDurations[] = {8,9,10,11,
                       8,9,10,11,
                       8,9,10,11,
                       8,9,10,11,
                       8,9,10,11
                     };

  // iterate over the notes of the melody:
  for (int thisNote = 0; thisNote < 8; thisNote++) 
  {
    // to calculate the note duration, take one second 
    // divided by the note type.
    //e.g. quarter note = base_millis / 4, eighth note = base_millis/8, etc.
    int noteDuration = base_millis/noteDurations[thisNote];
    //tone(11, melody[thisNote],noteDuration);
    TV.tone(melody[thisNote],noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    //noTone(11);
    TV.noTone();
  }   

}

void horn()
{
  TV.tone(NOTE_C4);
  digitalWrite(TIMELED_PIN, HIGH);
  delay(3500);
  TV.noTone();
  digitalWrite(TIMELED_PIN, LOW);
}
